"""
客户段可以是多样的，PC端，网页端，小程序...
网页端的输入 <input />
cmd终端：input()
功能类的设计尽量不要出现input和print， 我们要用的是参数，返回值 交给可视化的客户端程序来完成具体的输入和输出

功能程序

"""
import asyncio
import os
import re
from typing import List

import aiohttp
import motor
import motor.motor_asyncio

from xunter_utils.xunter_requests import ChromeClient


# 创建一个BQG爬虫的类  调用这个类就可以进行笔趣阁的爬虫功能了
# BqgReptileApi Api功能类 应用程序接口功能
class BQGClient(ChromeClient):
    def __init__(self, download_path: str = 'Download') -> None:
        """
        初始化方法
        :param download_path:下载小说的保存目录
        """
        super().__init__()
        # 将初始化的参数存到属性当中
        self.download_path = download_path
        self.host = 'https://www.biqu01.cc/'  # 域名地址抽离

        self.connection = motor.motor_asyncio.AsyncIOMotorClient(host='127.0.0.1', port=27017)
        self.db = self.connection['BQGReptile']
        self.collection = self.db['BQG_novel']

        # 判断是否存在文件夹，不存在就创建
        if not os.path.isdir(download_path):
            os.mkdir(download_path)

    def search_book(self, name: str) -> List:
        """
        搜索小说
        :param name:小说名  因为网页地址中 在域名地址后接的是 参数 小说名
        :return: 搜索到的小说列表
        """

        """
        获取cookie
        这里要注意  抓包时既要找到带有Set-Cookie的请求地址还要看这个请求地址返回的值是我们需要的返回值
        这里抓包先找到我们需要的返回值的地址文件(这里就根据我们搜索后的得到的数据找)  找到后看到里面没有set-cookie
        但是cookie的参数一看感觉比其他的多了点参数就可以到其他带有这个参数的文件中找
        这个文件可能有多个这时就需要根据搜索前的我们输入的数据找(看 有效荷载(payload)、preview(预览)、Response(响应) 跟搜索前是一样的就没错了)
        """
        self.send_request(f'{self.host}/user/hm.html', params={
            'q': name  # 参数为问号后面的值  在Payload中查看是否有参数
        })

        # 搜索小说
        res = self.send_request(f'{self.host}/user/search.html', params={
            'q': name
        })
        if res:
            return res.json()  # 将返回值进行json格式转换
        return []

    def get_pages(self, url: str) -> List:
        """
        获取指定小说的章节
        :param url: 小说资源地址  因为网页地址中 在域名地址后接的是小说的资源地址 '/book/15944/'
        :return:解析后的章节列表  [(章节网址,章节标题),(章节网址,章节标题)]
        """
        res = self.send_request(f'{self.host}{url}')

        if not res:
            return []

        return re.findall(r'<dd><a href ="(.*?)">(.*?)</a></dd>', res.text)

    def download_page(self, url: str) -> str:
        """
        下载指定页面的内容
        :param url:小说页面地址 /book/15944/1.html
        :return:小说文本
        """

        book_res = self.send_request(f'{self.host}{url}')
        if not book_res:
            return ''

        chapter_conet = re.findall(
            r' class="Readarea ReadAjax_content">(.*?)请收藏本站：https://www.bqg128\.com',
            book_res.text
        )

        res = ''
        for i in chapter_conet:
            res += i.replace(r'<br /><br />', '\n')

        return res

    async def async_download_page(
            self,
            name: str,
            index: int,
            url: str,
            title: str,
            session: aiohttp.ClientSession,
            semaphore: asyncio.Semaphore,
    ) -> str:
        """
        异步下载小说的章节
        :param semaphore: 锁对象
        :param session:异步请求对象
        :param index:索引
        :param url:地址
        :param title:标题
        :return:
        """

        print('title', title)

        # 定义一个临时目录 保存下好的章节 全部下载好后就合并 因为异步任务是同时进行的不存在先后顺序关系 下载的小说章节也是没有顺序的
        temp_path = 'temp'
        if not os.path.isdir(temp_path):
            os.mkdir(temp_path)
        # await会等待后面跟着的异步任务执行完成，同时切换开始调用的别的任务
        # 也就是说当代码执行到这一行时就会有一个await阻塞并且让后面跟着的任务去往下一个协程函数中执行任务
        # 总结就是在会阻塞并且需要得到返回值的地方加上await  在它的上一层要加上async

        async with semaphore:
            async with await session.get(f'{self.host}{url}') as book_res:
                text = await book_res.text()  # 等待响应编码后的数据 就是res.text() 拿编码后的结果这里也会发生阻塞所以要加await
                print('text', text)

                chapter_conet = re.findall(
                    r' class="Readarea ReadAjax_content">(.*?)请收藏本站：https://www.biqu01.cc',
                    text
                )
                print('内容',chapter_conet)
                res = title + '\n'
                for i in chapter_conet:
                    res += i.replace(r'<br /><br />', '\n')

                # 存入Mongodb
                novel_data = {
                    "name": name,
                    "index": index,
                    "title": title,
                    "url": url,
                    "content": res,
                }
                await self.collection.insert_one(novel_data)
                print(name, index, title, '写入完成')

                # 文件拼接把下好的小说章节文件放到temp_path文件夹中
                # 保存的文件名格式为 1_第一章.txt  先分割成(1,第一章) 再用sort(key=lambda x:x[0])按照第一个元素做排序
                # 把文件按章节排序好之后再合并就完整了
                with open(os.path.join(
                        temp_path, f'{index}_{title}.txt'
                ), 'w', encoding='utf-8') as f:
                    f.write(res)

                print(index, title, '写入完成')

    def txt_download_page(self, index: int, url: str, title: str, ):
        temp_path = 'temp1'
        if not os.path.isdir(temp_path):
            os.mkdir(temp_path)

        book_res = self.send_request(f'{self.host}{url}')
        if not book_res:
            return ''

        chapter_conet = re.findall(
            r' class="Readarea ReadAjax_content">(.*?)请收藏本站：https://www.bqg128\.com',
            book_res.text
        )

        res = title + '\n'
        for i in chapter_conet:
            res += i.replace(r'<br /><br />', '\n')

        with open(os.path.join(
                temp_path, f'{index}_{title}.txt'
        ), 'w', encoding='utf-8') as f:
            f.write(res)

        print(index, title, '写入完成')

#     # 单元测试
#     def test(self):
#         # print(self.session)
#         """
#         这里会生成logs文件夹是因为在继承ChromeClient类加载ChromeClient类时装饰器会自动执行了所以就自动生成了logs文件夹
#         """
#
#
# 测试
# if __name__ == '__main__':
#     client = BQGClient()
#     # client.test()   #测试发送请求模块是否正常
#
#     # res = client.search_book('万相之王')   #测搜索功能是否成功
#     res = client.get_pages('/book/15944/')  #测试小说章节获取是否成功
#     print(res)
#     # res = client.download_page('/book/15944/1.html')  #测试指定小说页面获取是否成功
# #
